﻿using AutoCommunication.Base;
using AutoCommunication.Enums;
using AutoCommunication.Helper;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace AutoCommunication.Clients
{
    public class DevModbusRtu : BaseSerialPortDevice<VarableModbus>
    {
        #region 构造函数

        public DevModbusRtu()
        {
        }

        #endregion 构造函数

        #region 属性

        [JsonProperty("设备名"), DisplayName("设备名"), Category("设备信息")]
        public override string Name { get; set; } = "ModbusRtu";

        [JsonProperty("字节格式"), DisplayName("字节格式"), Category("设备信息")]
        public override EndianFormat Format { get; set; } = EndianFormat.DCBA;

        #endregion 属性

        protected override byte[] GetWriteCommand(Group<VarableModbus> group)
        {
            var info = group.Varablelist[0];
            byte[] buffer = null;
            ushort startaddress;
            if (plcAddresses)
            {
                startaddress = Convert.ToUInt16(((ushort)group.StartAddress) % 10000 - 1);
            }
            else
            {
                startaddress = (ushort)group.StartAddress;
            }
            switch (info.ModbusStore)
            {
                case ModbusStore.InCoil:
                case ModbusStore.InRegister:
                    throw new Exception("不能向输入线圈|输入寄存器写数据哦");
                case ModbusStore.OutCoil:
                    if (group.Varablelist.Count == 1) //写单个线圈
                    {
                        buffer = new byte[6];
                        buffer[0] = info.StationNumber; //站号
                        buffer[1] = (byte)ModbusFunCode.WriteSingleCoil;  //功能码
                        buffer[2] = BitConverter.GetBytes(startaddress)[1];
                        buffer[3] = BitConverter.GetBytes(startaddress)[0];//寄存器地址
                                                                           //  var value = Convert.ToBoolean(group.Varablelist.First().Value);
                        var value = Convert.ToBoolean(group.Varablelist.First().WriteValue);
                        buffer[4] = (byte)(value ? 0xFF : 0x00);
                        buffer[5] = 0x00;//固定00
                        return CRC16.GetCRC16(buffer);
                    }
                    else if (group.Varablelist.Count >= 2)//写多个线圈
                    {
                        var mod = group.Varablelist.Count % 8 == 0;
                        var count = (byte)(mod ? group.Varablelist.Count / 8 : group.Varablelist.Count / 8 + 1);
                        buffer = new byte[7 + count];
                        buffer[0] = info.StationNumber; //站号
                        buffer[1] = (byte)ModbusFunCode.WriteMutilCoil;  //功能码
                        buffer[2] = BitConverter.GetBytes(startaddress)[1];
                        buffer[3] = BitConverter.GetBytes(startaddress)[0];//寄存器起始地址
                        buffer[4] = (byte)(group.Varablelist.Count / 256);
                        buffer[5] = (byte)(group.Varablelist.Count % 256);//写的线圈的个数
                        buffer[6] = count;//之后的字节个数:计算公式为我们要写的线圈个数/8
                        byte[] vals = new byte[buffer[6]];
                        for (int i = 0; i < group.Varablelist.Count; i++)
                        {
                            vals[i / 8] = vals[i / 8].SetBit(i % 8, Convert.ToBoolean(group.Varablelist[i].Value));
                        }
                        vals.CopyTo(buffer, 7);
                        return CRC16.GetCRC16(buffer);
                    }
                    break;

                case ModbusStore.HoldRegister:
                    if (group.Varablelist.Count == 1 && (group.Varablelist.First().DataType == DataTypeEnum.Int16 || group.Varablelist.First().DataType == DataTypeEnum.UInt16))//写单个寄存器
                    {
                        buffer = new byte[6];
                        buffer[0] = info.StationNumber; //站号
                        buffer[1] = (byte)ModbusFunCode.WriteSingleRegister;  //功能码
                        buffer[2] = BitConverter.GetBytes(startaddress)[1];
                        buffer[3] = BitConverter.GetBytes(startaddress)[0];//寄存器地址
                        var vals = Convert.ToDouble(group.Varablelist[0].WriteValue).ToBytes(group.Varablelist.First().DataType, Format);
                        buffer[4] = vals[0];
                        buffer[5] = vals[1];
                        return CRC16.GetCRC16(buffer);
                    }
                    else
                    {
                        var count = 0;
                        foreach (var item in group.Varablelist)
                        {
                            count = count + Common.GetBytelength(item) / 2;
                        }
                        var vals = new byte[count * 2];
                        int i = 0;
                        foreach (var item in group.Varablelist)
                        {
                            var val = Convert.ToDouble(item.WriteValue).ToBytes(item.DataType, Format);
                            val.CopyTo(vals, i);
                            i = i + Common.GetBytelength(item);
                        }
                        buffer = new byte[7 + count * 2];
                        buffer[0] = info.StationNumber; //站号
                        buffer[1] = (byte)ModbusFunCode.WriteMutilRegister;  //功能码
                        buffer[2] = BitConverter.GetBytes(startaddress)[1];
                        buffer[3] = BitConverter.GetBytes(startaddress)[0];//寄存器地址
                        buffer[4] = (byte)(count / 256);
                        buffer[5] = (byte)(count % 256);//写寄存器数量
                        buffer[6] = (byte)(count * 2);
                        vals.CopyTo(buffer, 7);
                        return CRC16.GetCRC16(buffer);
                    }
            }
            return CRC16.GetCRC16(buffer);
        }

        protected override List<byte[]> GetReadCommand(Group<VarableModbus> group)
        {
            List<byte[]> result = new();
            var info = group.Varablelist[0];
            byte[] buffer = new byte[6];
            buffer[0] = info.StationNumber;  //站号
            switch (info.ModbusStore)
            {
                case ModbusStore.InCoil:
                    buffer[1] = 0x02;   //功能码
                    break;

                case ModbusStore.OutCoil:
                    buffer[1] = 0x01;   //功能码
                    break;

                case ModbusStore.InRegister:
                    buffer[1] = 0x04;   //功能码
                    break;

                case ModbusStore.HoldRegister:
                    buffer[1] = 0x03;   //功能码
                    break;
            }
            ushort startaddress;
            if (plcAddresses)
            {
                startaddress = Convert.ToUInt16(((ushort)group.StartByteAddress) % 10000 - 1);
                buffer[2] = BitConverter.GetBytes(startaddress)[1];
                buffer[3] = BitConverter.GetBytes(startaddress)[0];//寄存器地址
            }
            else
            {
                startaddress = (ushort)group.StartByteAddress;
                buffer[2] = BitConverter.GetBytes(startaddress)[1];
                buffer[3] = BitConverter.GetBytes(startaddress)[0];//寄存器地址
            }
            buffer[4] = BitConverter.GetBytes((ushort)group.Length)[1];//多少个寄存器?多少位?
            buffer[5] = BitConverter.GetBytes((ushort)group.Length)[0];
            result.Add(CRC16.GetCRC16(buffer));
            return result;
        }

        protected override Result<byte[]> SendAndRevice(byte[] command)
        {
            try
            {
                lock (Readlock)//todo 我不确定这里是范围定义的是否合适.
                {
                    //发送命令/
                    serialPort.Write(command, 0, command.Length);
                    //获取响应报文
                    Result<byte[]> result = serialPort.ReadAll();

                    if (!result.IsSucceed)
                    {
                        Loger?.Invoke(result.Err, result.Exception);
                        return result;
                    }
                    else
                    {
                        result.ResponseData = new byte[result.Value.Length - 5];
                        Array.Copy(result.Value, 3, result.ResponseData, 0, result.ResponseData.Length);
                        return result;
                    }
                }
            }
            catch (Exception ex)
            {
                Loger?.Invoke(ex.Message, ex);
                var result = new Result<byte[]>();
                result.Exception = ex;
                result.IsSucceed = false;
                result.Err = ex.Message;
                return result;
            }
        }

        protected override Result CheckDeviceParameters()
        {
            return new Result();
        }

        protected override Result CheckOther()
        {
            return new Result();
        }

        protected override List<Group<VarableModbus>> Group(List<VarableModbus> AllVarable)
        {
            Result result = new Result();
            List<Group<VarableModbus>> GroupList = new List<Group<VarableModbus>>();
            var g1 = AllVarable.GroupBy(x => x.StationNumber);//根据站号分组
            foreach (var group in g1)
            {
                var g2 = group.GroupBy(x => x.ModbusStore);//在根据存储区分组
                IOrderedEnumerable<VarableModbus> group2order = null;
                foreach (var group2 in g2)//相同站号下面不同的存储区
                {
                    Group<VarableModbus> gp = new Group<VarableModbus>();
                    switch (group2.Key)
                    {
                        case ModbusStore.InCoil:
                        case ModbusStore.OutCoil:
                            group2order = group2.OrderBy(x => x.StartByteAddress).ThenBy(x => x.BitOffset);//根据开始地址排序
                            gp.StartByteAddress = group2order.First().StartByteAddress;//将作为这个组的起始地址
                            gp.StartAddress = group2order.First().StartByteAddress * 8 + group2order.First().BitOffset;
                            gp.Length = 1;
                            GroupList.Add(gp);
                            foreach (var item in group2order)
                            {
                                //
                                if (item.StartByteAddress * 8 + item.BitOffset - GroupList.Last().StartAddress < MaxReadBits)////要测试
                                {
                                    GroupList.Last().Varablelist.Add(item);
                                    GroupList.Last().Length = (item.StartByteAddress * 8 + item.BitOffset - GroupList.Last().StartAddress) + 1;
                                }
                                else
                                {
                                    GroupList.Add(new Group<VarableModbus>()
                                    {
                                        StartByteAddress = item.StartByteAddress,
                                        Length = 1
                                    }); ;//超过大小,另外起一组,其中这个变量的起始地址就是这个组的起始地址
                                    GroupList.Last().Varablelist.Add(item);
                                }
                            }
                            break;

                        case ModbusStore.InRegister:
                        case ModbusStore.HoldRegister:
                            group2order = group2.OrderBy(x => x.StartByteAddress);//根据开始地址排序
                            gp.StartByteAddress = group2order.First().StartByteAddress;//
                            gp.StartAddress = group2order.First().StartByteAddress / 2;
                            var bytelength = Common.GetBytelength(group2order.First());
                            gp.Length = bytelength == 1 ? 1 : bytelength / 2;
                            GroupList.Add(gp);
                            foreach (var item in group2order)
                            {
                                //todo 精通后回来优化这里/已经优化4次
                                if (item.StartByteAddress - GroupList.Last().StartByteAddress < MaxReadBytes)
                                {
                                    //最多125?124?123个寄存器,保险起见,我用123个,也就是123个寄存器;也就是246个字节一次是,因为返回的报文中,第三个字节表示这次报文一共读取到了多少个,所以最多就2^8=256个字节;128个寄存器,返回的报文 站号1,功能码1,字节总数1,数据n.校验2
                                    var bytelength2 = Common.GetBytelength(item) == 1 ? 1 : Common.GetBytelength(item) / 2;
                                    GroupList.Last().Length = item.StartByteAddress / 2 - GroupList.Last().StartByteAddress / 2 + bytelength2;
                                    GroupList.Last().Varablelist.Add(item);
                                }
                                else
                                {
                                    GroupList.Add(new Group<VarableModbus>()
                                    {
                                        StartByteAddress = item.StartByteAddress,
                                        StartAddress = item.StartByteAddress / 2,
                                        Length = Common.GetBytelength(item) == 1 ? 1 : Common.GetBytelength(item) / 2
                                    }); ;//超过大小,另外起一组,其中这个变量的起始地址就是这个组的起始地址
                                    GroupList.Last().Varablelist.Add(item);
                                }
                            }
                            break;
                    }
                }
            }
            return GroupList;
        }

        protected override Result AutoReadGroup()
        {
            Result result = new Result();
            return result;
        }
    }

    #region 派生的变量和变量组

    public class VarableModbus : BaseVariable
    {
        public VarableModbus(string address, DataTypeEnum dataType)
        {
            DataType = dataType;
            Address = address.Norm();
            // AddressAnalysis().ShowMessageBox();
        }

        [JsonProperty("存储区"), DisplayName("2.2存储区"), Category("变量信息")]
        public ModbusStore ModbusStore { get; set; } = ModbusStore.HoldRegister;

        /// <summary>
        /// 站号
        /// </summary>
        [JsonProperty("站号"), DisplayName("2.1站号"), Category("变量信息")]
        public byte StationNumber { get; set; } = 1;

        /// <summary>
        /// 变量地址
        /// </summary>
        [JsonProperty("变量地址"), DisplayName("地址"), Category("变量信息")]
        public override string Address { get; set; } = "十进制格式的地址";

        public override Result AddressAnalysis()
        {
            Result result = new Result();
            Address = Address.Norm();
            try
            {
                switch (Address[0])
                {
                    case '0':
                        ModbusStore = ModbusStore.OutCoil;
                        break;

                    case '1':
                        ModbusStore = ModbusStore.InCoil;
                        break;

                    case '3':
                        ModbusStore = ModbusStore.InRegister;
                        break;

                    case '4':
                        ModbusStore = ModbusStore.HoldRegister;
                        break;

                    default:
                        result.IsSucceed = false;
                        result.Err = $"{Address}地址格式不对";
                        return result;
                }
                var res = Address.Split('.');
                if (res.Length > 1)
                {
                    StationNumber = byte.Parse(res[1]);
                }
                var start = int.Parse(res[0]) % 10000 - 1;
                if (ModbusStore == ModbusStore.InCoil || ModbusStore == ModbusStore.OutCoil)
                {
                    StartByteAddress = start / 8;

                    BitOffset = start % 8;
                }
                else
                {
                    StartByteAddress = start * 2;
                }
            }
            catch (Exception ex)
            {
                result.IsSucceed = false;
                result.Exception = ex;
                return result;
            }
            return result;
        }
    }

    #endregion 派生的变量和变量组
}